package com.example.docmanagement.service;

import com.example.docmanagement.config.MinioConfig;
import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.Bucket;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * MinIO 对象存储服务
 * 负责与 MinIO 服务器的所有交互
 * 
 * MinIO 服务器信息：
 * - 地址：192.168.1.86:9000
 * - 账号：esun
 * - 密码：12345678
 * 
 * 核心功能：
 * - 文件上传（单文件、批量、文本）
 * - 文件下载
 * - 文件删除
 * - URL 生成（公开 URL、临时 URL）
 * - Bucket 管理
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class MinioService {

    private final MinioClient minioClient;
    private final MinioConfig minioConfig;

    // ============================================================================
    // 文件上传
    // ============================================================================

    /**
     * 上传文件到 MinIO
     * 
     * @param file       上传的文件
     * @param bucketName Bucket 名称
     * @return MinIO 文件路径（bucket/year/month/uuid.ext）
     */
    public String uploadFile(MultipartFile file, String bucketName) throws Exception {
        // 确保 Bucket 存在
        ensureBucketExists(bucketName);

        // 生成唯一文件名
        String objectKey = generateObjectKey(file.getOriginalFilename());
        String filePath = bucketName + "/" + objectKey;

        // 上传文件
        minioClient.putObject(
            PutObjectArgs.builder()
                .bucket(bucketName)
                .object(objectKey)
                .stream(file.getInputStream(), file.getSize(), -1)
                .contentType(file.getContentType())
                .build()
        );

        log.info("文件上传成功: {}", filePath);
        return filePath;
    }

    /**
     * 上传字节数组到 MinIO
     * 
     * @param bytes      字节数组
     * @param bucketName Bucket 名称
     * @param fileName   文件名（用于生成扩展名）
     * @param contentType MIME 类型
     * @return MinIO 文件路径
     */
    public String uploadBytes(byte[] bytes, String bucketName, String fileName, String contentType) throws Exception {
        ensureBucketExists(bucketName);

        String objectKey = generateObjectKey(fileName);
        String filePath = bucketName + "/" + objectKey;

        InputStream inputStream = new ByteArrayInputStream(bytes);

        minioClient.putObject(
            PutObjectArgs.builder()
                .bucket(bucketName)
                .object(objectKey)
                .stream(inputStream, bytes.length, -1)
                .contentType(contentType)
                .build()
        );

        log.info("字节数组上传成功: {}, 大小: {} bytes", filePath, bytes.length);
        return filePath;
    }

    /**
     * 上传文本内容到 MinIO（用于 Markdown 文件）
     * 
     * @param content    文本内容
     * @param bucketName Bucket 名称
     * @param fileName   文件名（可选，自动生成 .md 扩展名）
     * @return MinIO 文件路径
     */
    public String uploadText(String content, String bucketName, String fileName) throws Exception {
        ensureBucketExists(bucketName);

        // 如果没有提供文件名，生成默认的 .md 文件名
        if (fileName == null || fileName.isEmpty()) {
            fileName = UUID.randomUUID().toString() + ".md";
        }

        byte[] bytes = content.getBytes(StandardCharsets.UTF_8);
        String objectKey = generateObjectKey(fileName);
        String filePath = bucketName + "/" + objectKey;

        InputStream inputStream = new ByteArrayInputStream(bytes);

        minioClient.putObject(
            PutObjectArgs.builder()
                .bucket(bucketName)
                .object(objectKey)
                .stream(inputStream, bytes.length, -1)
                .contentType("text/markdown; charset=utf-8")
                .build()
        );

        log.info("Markdown 文本上传成功: {}, 大小: {} bytes", filePath, bytes.length);
        return filePath;
    }

    // ============================================================================
    // 文件下载
    // ============================================================================

    /**
     * 下载文件（返回输入流）
     * 
     * @param filePath MinIO 文件路径（bucket/year/month/uuid.ext）
     * @return 文件输入流
     */
    public InputStream downloadFile(String filePath) throws Exception {
        String[] parts = filePath.split("/", 2);
        String bucketName = parts[0];
        String objectKey = parts[1];

        return minioClient.getObject(
            GetObjectArgs.builder()
                .bucket(bucketName)
                .object(objectKey)
                .build()
        );
    }

    /**
     * 下载文本内容（用于 Markdown 文件）
     * 
     * @param filePath MinIO 文件路径
     * @return 文本内容
     */
    public String downloadText(String filePath) throws Exception {
        try (InputStream stream = downloadFile(filePath)) {
            return new String(stream.readAllBytes(), StandardCharsets.UTF_8);
        }
    }

    // ============================================================================
    // 文件删除
    // ============================================================================

    /**
     * 删除文件
     * 
     * @param filePath MinIO 文件路径
     */
    public void deleteFile(String filePath) throws Exception {
        String[] parts = filePath.split("/", 2);
        String bucketName = parts[0];
        String objectKey = parts[1];

        minioClient.removeObject(
            RemoveObjectArgs.builder()
                .bucket(bucketName)
                .object(objectKey)
                .build()
        );

        log.info("文件删除成功: {}", filePath);
    }

    // ============================================================================
    // URL 生成
    // ============================================================================

    /**
     * 获取公开访问 URL
     * 
     * @param filePath MinIO 文件路径
     * @return 完整的访问 URL
     */
    public String getPublicUrl(String filePath) {
        return minioConfig.getEndpoint() + "/" + filePath;
    }

    /**
     * 生成临时访问 URL（Presigned URL）
     * 用于私有文件的临时访问
     * 
     * @param filePath      MinIO 文件路径
     * @param expirySeconds 过期时间（秒），最大 7 天
     * @return Presigned URL
     */
    public String getPresignedUrl(String filePath, int expirySeconds) throws Exception {
        String[] parts = filePath.split("/", 2);
        String bucketName = parts[0];
        String objectKey = parts[1];

        // 限制最大过期时间为 7 天
        int maxExpiry = 7 * 24 * 60 * 60;
        int actualExpiry = Math.min(expirySeconds, maxExpiry);

        return minioClient.getPresignedObjectUrl(
            GetPresignedObjectUrlArgs.builder()
                .method(Method.GET)
                .bucket(bucketName)
                .object(objectKey)
                .expiry(actualExpiry, TimeUnit.SECONDS)
                .build()
        );
    }

    // ============================================================================
    // Bucket 管理
    // ============================================================================

    /**
     * 确保 Bucket 存在，不存在则创建
     * 
     * @param bucketName Bucket 名称
     */
    public void ensureBucketExists(String bucketName) throws Exception {
        boolean exists = minioClient.bucketExists(
            BucketExistsArgs.builder()
                .bucket(bucketName)
                .build()
        );

        if (!exists) {
            minioClient.makeBucket(
                MakeBucketArgs.builder()
                    .bucket(bucketName)
                    .build()
            );
            log.info("Bucket 创建成功: {}", bucketName);
        }
    }

    /**
     * 设置 Bucket 为公开访问
     * 
     * @param bucketName Bucket 名称
     */
    public void setBucketPublic(String bucketName) throws Exception {
        // Java 11 兼容的字符串拼接
        String policy = String.format(
            "{" +
            "  \"Version\": \"2012-10-17\"," +
            "  \"Statement\": [" +
            "    {" +
            "      \"Effect\": \"Allow\"," +
            "      \"Principal\": {\"AWS\": [\"*\"]}," +
            "      \"Action\": [\"s3:GetObject\"]," +
            "      \"Resource\": [\"arn:aws:s3:::%s/*\"]" +
            "    }" +
            "  ]" +
            "}",
            bucketName
        );

        minioClient.setBucketPolicy(
            SetBucketPolicyArgs.builder()
                .bucket(bucketName)
                .config(policy)
                .build()
        );

        log.info("Bucket 设置为公开访问: {}", bucketName);
    }

    /**
     * 列出所有 Bucket
     * 
     * @return Bucket 列表
     */
    public List<Bucket> listBuckets() throws Exception {
        return minioClient.listBuckets();
    }

    /**
     * 初始化所有需要的 Buckets
     * 应用启动时调用
     */
    public void initializeBuckets() {
        try {
            // 创建 Buckets
            MinioConfig.BucketConfig buckets = minioConfig.getBuckets();
            
            ensureBucketExists(buckets.getDocImages());
            ensureBucketExists(buckets.getDocContents());
            ensureBucketExists(buckets.getDocAttachments());
            ensureBucketExists(buckets.getUserAvatars());
            ensureBucketExists(buckets.getThumbnails());
            ensureBucketExists(buckets.getTempUploads());

            // 设置公开访问策略
            setBucketPublic(buckets.getDocImages());
            setBucketPublic(buckets.getUserAvatars());
            setBucketPublic(buckets.getThumbnails());
            // doc-contents 和 doc-attachments 保持私有，按需生成 Presigned URL

            log.info("MinIO Buckets 初始化成功");
        } catch (Exception e) {
            log.error("MinIO Buckets 初始化失败", e);
            throw new RuntimeException("MinIO 初始化失败", e);
        }
    }

    // ============================================================================
    // 工具方法
    // ============================================================================

    /**
     * 生成对象键（Object Key）
     * 格式：year/month/uuid.ext
     * 示例：2024/11/a1b2c3d4-e5f6-7890-abcd-ef1234567890.jpg
     * 
     * @param originalFilename 原始文件名
     * @return 对象键
     */
    private String generateObjectKey(String originalFilename) {
        LocalDate now = LocalDate.now();
        String uuid = UUID.randomUUID().toString();
        String extension = getFileExtension(originalFilename);
        
        return String.format("%d/%02d/%s%s",
            now.getYear(),
            now.getMonthValue(),
            uuid,
            extension
        );
    }

    /**
     * 获取文件扩展名
     * 
     * @param filename 文件名
     * @return 扩展名（含点，如 .jpg）
     */
    private String getFileExtension(String filename) {
        if (filename == null || filename.isEmpty()) {
            return "";
        }
        int dotIndex = filename.lastIndexOf('.');
        if (dotIndex == -1) {
            return "";
        }
        return filename.substring(dotIndex);
    }

    /**
     * 检查文件是否存在
     * 
     * @param filePath MinIO 文件路径
     * @return 是否存在
     */
    public boolean fileExists(String filePath) {
        try {
            String[] parts = filePath.split("/", 2);
            String bucketName = parts[0];
            String objectKey = parts[1];

            minioClient.statObject(
                StatObjectArgs.builder()
                    .bucket(bucketName)
                    .object(objectKey)
                    .build()
            );
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 获取文件信息
     * 
     * @param filePath MinIO 文件路径
     * @return 文件统计信息
     */
    public StatObjectResponse getFileInfo(String filePath) throws Exception {
        String[] parts = filePath.split("/", 2);
        String bucketName = parts[0];
        String objectKey = parts[1];

        return minioClient.statObject(
            StatObjectArgs.builder()
                .bucket(bucketName)
                .object(objectKey)
                .build()
        );
    }
}

